package cn.xinfei.xdecision.common.service.enginex.impl;

import cn.xinfei.xdecision.common.dao.enginex.EngineVersionMapper;
import cn.xinfei.xdecision.common.model.component.dependency.EngineNodeComponentRel;
import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VariableVO;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.vo.VariableDepVO;
import cn.xinfei.xdecision.common.model.enginex.po.EngineAction;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineUpdateReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionInOutVarsRespVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineVersionRespVo;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.common.sso.SSOService;
import cn.xinfei.xdecision.common.service.component.dependency.AbstractDependency;
import cn.xinfei.xdecision.common.service.component.dependency.EngineNodeComponentRelService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.impl.VariableServiceImpl;
import cn.xinfei.xdecision.common.service.enginex.EngineNodeService;
import cn.xinfei.xdecision.common.service.enginex.EngineService;
import cn.xinfei.xdecision.common.service.enginex.EngineVersionService;
import cn.xinfei.xdecision.common.utils.constant.CommonConst;
import cn.xinfei.xdecision.common.utils.constant.enginex.EngineNodeConst;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.CollectionUtil;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.*;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

@Service
@Slf4j
@RefreshScope
public class EngineVersionServiceImpl extends ServiceImpl<EngineVersionMapper, EngineVersion> implements EngineVersionService {

    @Autowired
    private EngineVersionMapper engineVersionMapper;
    @Resource
    private EngineNodeService engineNodeService;
    @Autowired
    private SSOService ssoService;

    @Resource
    private EngineNodeComponentRelService engineNodeComponentRelService;

    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private VariableServiceImpl variableService;
    @Resource
    private DecisionFlowCheckService decisionFlowCheckService;

    @Value("${xdecision.decisionFlow.preOperationSafetyCheck:false}")
    private boolean preOperationSafetyCheck;

    @Resource
    private EngineService engineService;

    @Resource
    private EngineActionServiceImpl engineActionService;

    private QueryWrapper<EngineVersion> queryWrapper(String engineCode) {
        QueryWrapper<EngineVersion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("engine_code", engineCode);
        return queryWrapper;
    }

    private QueryWrapper<EngineVersion> queryWrapper(String engineCode, int status) {
        QueryWrapper<EngineVersion> queryWrapper = queryWrapper(engineCode);
        queryWrapper.eq("status", status);
        return queryWrapper;
    }

    private QueryWrapper<EngineVersion> queryWrapperByVersionNo(String engineCode, long versionNo) {
        QueryWrapper<EngineVersion> queryWrapper = queryWrapper(engineCode);
        queryWrapper.eq("version_no", versionNo);
        return queryWrapper;
    }

    @Override
    public EngineVersion getById(Long id) {
        return super.getById(id);
    }

    @Override
    public Long getMaxVersionNo(String engineCode) {
        return engineVersionMapper.getMaxVersionNo(engineCode);
    }

    @Override
    public EngineVersion getByEngineCodeAndVersionNo(String engineCode, Long versionNo) {
        return engineVersionMapper.getVersionNodes(engineCode, versionNo);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deployEngine(Long loginUserId, EngineVersionReqVo engineVersionReqVo) {
        String engineCode = engineVersionReqVo.getEngineCode();
        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(engineCode);
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);
        Long versionNo = engineVersionReqVo.getVersionNo();

        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineCode, versionNo);
        if (engineVersion != null) {
            int currentVersionStatus = engineVersion.getStatus();
            if (currentVersionStatus == EngineVersionStatusEnum.ENABLE.getStatus()) {
                throw new ApiException(ENGINE_DEPLOY_FAILED.getCode(), "当前版本正在运行中");
            }

            Long updateUserId = engineVersion.getUpdateUserId();
            if (updateUserId != null && updateUserId.equals(loginUserId)) {
                throw new ApiException(ENGINE_DEPLOY_FAILED.getCode(), "审核人不可以是保存人");
            }

            // 决策流部署前置安全性校验
            if (preOperationSafetyCheck) {
                decisionFlowCheckService.decisionFlowPreOperationSafetyCheck(engineCode, engineVersion.getId(), engineVersion.getEngineNodeList());
            }

            //查看是否有正在运行的版本,如果有,将其模型设置为未部署
            EngineVersion runningVersion = getRunningVersion(engineCode);
            if (runningVersion != null) {
                runningVersion.setStatus(EngineVersionStatusEnum.DISABLE.getStatus());
                this.updateById(runningVersion);
            }
            engineVersion.setStatus(EngineVersionStatusEnum.ENABLE.getStatus());
            return this.updateById(engineVersion);
        }
        throw new ApiException(ENGINE_DEPLOY_FAILED.getCode(), ENGINE_DEPLOY_FAILED.getMessage() + ",未查询到版本信息");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean undeployEngine(EngineVersionReqVo engineVersionReqVo) {
        String engineCode = engineVersionReqVo.getEngineCode();
        Long versionNo = engineVersionReqVo.getVersionNo();

        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineCode, versionNo);
        if (engineVersion != null) {
            engineVersion.setStatus(EngineVersionStatusEnum.DISABLE.getStatus());
            return this.updateById(engineVersion);
        }

        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(engineCode);
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);

        throw new ApiException(ENGINE_UNDEPLOY_FAILED.getCode(), ENGINE_UNDEPLOY_FAILED.getMessage() + ",未查询到版本信息");
    }

    @Override
    public List<EngineVersionRespVo> getEngineVersionListByEngineCode(String engineCode) {
        List<EngineVersion> engineVersions = this.list(queryWrapper(engineCode));
        List<EngineVersionRespVo> engineVersionRespVos = new ArrayList<>();

        if (engineVersions.isEmpty()) {
            return engineVersionRespVos;
        }

        engineVersions.forEach(ev -> {
            EngineVersionRespVo engineVersionRespVo = new EngineVersionRespVo();
            BeanUtils.copyProperties(ev, engineVersionRespVo);
            engineVersionRespVo.setVersionNoDesc("V" + engineVersionRespVo.getVersionNo());
            engineVersionRespVos.add(engineVersionRespVo);
        });
        ssoService.fillUserInfoToVO(engineVersionRespVos);

        return engineVersionRespVos;
    }

    @Override
    public EngineVersion copyAndCreateVersion(EngineVersionReqVo engineVersionReqVo) {
        String engineCode = engineVersionReqVo.getEngineCode();
        Long currentVersionNo = engineVersionReqVo.getVersionNo();

        //1. 判断当前是否存在【新建】状态版本
        List<EngineVersion> newStatusVersions = this.list(queryWrapper(engineCode, EngineVersionStatusEnum.NEW.getStatus()));
        if (CollectionUtils.isNotEmpty(newStatusVersions)) {
            throw new ApiException(ErrorCodeEnum.ENGINE_NEW_STATE_VERSION_EXISTS.getCode(), ErrorCodeEnum.ENGINE_NEW_STATE_VERSION_EXISTS.getMessage());
        }

        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineCode, currentVersionNo);
        if (engineVersion != null) {

            Long maxVersionNo = getMaxVersionNo(engineCode);
            Long newVersionNo = maxVersionNo + 1;
            //组装版本信息
            EngineVersion newVersion = new EngineVersion();
            newVersion.setStatus(EngineVersionStatusEnum.NEW.getStatus());
            newVersion.setEngineCode(engineCode);
            newVersion.setVersionNo(newVersionNo);
            Boolean f = transactionTemplate.execute(status -> {
                try {
                    if (engineVersionMapper.insert(newVersion) < 0) {
                        log.error("决策流节点版本创建失败，req====>{}", JSON.toJSONString(newVersion));
                        return Boolean.FALSE;
                    }
                    List<EngineNode> nodeList = engineVersion.getEngineNodeList();
                    Map<String, Long> nodeMap = new HashMap<>();
                    //复制节点和依赖关系
                    for (EngineNode engineNode : nodeList) {
                        // 清空原有父节点id
                        EngineNode newNode = new EngineNode();
                        BeanUtils.copyProperties(engineNode, newNode);
                        newNode.setParentId(null);
                        newNode.setVersionId(newVersion.getId());
                        engineNodeService.saveEngineNode(newNode);
                        nodeMap.put(engineNode.getNodeCode(), newNode.getId());
                        //依赖关系植入,只要有一个失败整体就失败
                        AbstractDependency targetDependency = AbstractDependency.getAbstractDependencyMap().get(newNode.getNodeType());
                        if (Objects.nonNull(targetDependency) &&
                                StringUtils.isNotEmpty(newNode.getNodeJson()) &&
                                Boolean.FALSE.equals(targetDependency.saveOrUpdateDependency(newNode))
                        ) {
                            log.error("依赖关系植入失败，req====>{}", JSON.toJSONString(newNode));
                            return Boolean.FALSE;
                        }
                        //如果有动作节点，则复制
                        if (Objects.equals(newNode.getNodeType(), NodeTypeEnum.ACTION.getType())) {
                            EngineAction oldEngineAction = engineActionService.getOne(new LambdaQueryWrapper<EngineAction>()
                                    .eq(EngineAction::getVersionId, engineNode.getVersionId())
                                    .eq(EngineAction::getNodeCode, engineNode.getNodeCode()));
                            if (Objects.nonNull(oldEngineAction)) {
                                oldEngineAction.setVersionId(newVersion.getId()).setId(null);
                                if (!engineActionService.save(oldEngineAction)) {
                                    log.error("动作节点植入失败，req====>{}", JSON.toJSONString(newNode));
                                    return Boolean.FALSE;
                                }
                            }
                        }
                    }

                    //更新节点父id
                    for (EngineNode engineNode : nodeList) {
                        String parentId = engineNode.getParentId();
                        if (!StringUtil.isValidStr(parentId)) {
                            continue;
                        }

                        String[] parentIdList = parentId.split(CommonConst.SYMBOL_COMMA);
                        Set<Long> newParentIdList = new HashSet<>();
                        for (String parentId0 : parentIdList) {
                            if (EngineNodeConst.REMOVE_LINK_PARENT_ID.equals(parentId0)) {
                                continue;
                            }
                            EngineNode engineNode1 = engineNodeService.getNodeById(Long.valueOf(parentId0));
                            long newId = nodeMap.get(engineNode1.getNodeCode());
                            newParentIdList.add(newId);
                        }

                        String newParentId = StringUtils.join(newParentIdList, CommonConst.SYMBOL_COMMA);
                        EngineNode e = EngineNode.builder().parentId(newParentId).build();
                        if (!engineNodeService.updateByNodeCodeAndVersionId(engineNode.getNodeCode(), newVersion.getId(), e)) {
                            log.error("更新节点父id失败，req====>{}", JSON.toJSONString(engineNode));
                            return Boolean.FALSE;
                        }
                    }

                    //更新主表的更新人和时间
                    EngineUpdateReqVo engine = new EngineUpdateReqVo();
                    engine.setEngineCode(engineCode);
                    engineService.updateEngine(SessionManager.getLoginAccount(), engine);

                    return Boolean.TRUE;
                } catch (Exception e) {
                    status.setRollbackOnly();
                    log.error("复制当前版本并创建新版本失败，req==>{}", JSON.toJSONString(engineVersionReqVo), e);
                    throw new ApiException(ErrorCodeEnum.DECISION_COPY_ERROR);
                }
            });
            if (Boolean.FALSE.equals(f)) {
                throw new ApiException(ErrorCodeEnum.DECISION_COPY_ERROR);
            }

            return getByEngineCodeAndVersionNo(engineCode, newVersionNo);

        }
        throw new ApiException(DATA_IS_NOT_EXIST.getCode(), DATA_IS_NOT_EXIST.getMessage());
    }


    public boolean clear(Long versionId) {
        // 所有节点分为四类：
        // 第一类开始/结束节点，只需要更新nextNode,
        // 第二类评分卡和规则节点,需要删除知识库映射关系,
        // 第四类,普通节点直接删除
        List<Long> commons = new ArrayList<>();

        List<EngineNode> engineNodes = engineNodeService.getEngineNodeListByEngineVersionId(versionId);
        if (CollectionUtil.isNotNullOrEmpty(engineNodes)) {
            for (EngineNode engineNode : engineNodes) {
                NodeTypeEnum nodeTypeEnum = NodeTypeEnum.adapad(engineNode.getNodeType());
                if (nodeTypeEnum == null) {
                    return false;
                }
                switch (nodeTypeEnum) {
                    case START:
                        engineNode.setNextNodeCodes("");
                        engineNodeService.updateById(engineNode);
                        break;
                    case END:
                        engineNode.setParentId("");
                        engineNodeService.updateById(engineNode);
                        break;
                    case RULE_BASE:
                    case SCORECARD:
                        //规则，评分卡
                        commons.add(engineNode.getId());
                        break;
                    default:
                        commons.add(engineNode.getId());
                        break;
                }
            }

            //第四步,删除节点和删除依赖关系
            if (CollectionUtil.isNotNullOrEmpty(commons)) {
                List<EngineNodeComponentRel> list = engineNodes.stream().map(x -> {
                    EngineNodeComponentRel dependency = new EngineNodeComponentRel();
                    dependency.setEngineVersionId(x.getVersionId());
                    dependency.setEngineNodeCode(x.getNodeCode());
                    return dependency;
                }).collect(Collectors.toList());
                //删除依赖关系
                engineNodeComponentRelService.removeRel(list);
                //删除动作表
                engineActionService.remove(new LambdaQueryWrapper<EngineAction>().eq(EngineAction::getVersionId, versionId));
                //删除节点
                if (!engineNodeService.removeNodesByIds(commons)) {
                    throw new ApiException(ErrorCodeEnum.ENGINE_REMOVE_ERROR);
                }
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean clear(EngineVersionReqVo engineVersionReqVo) {
        String engineCode = engineVersionReqVo.getEngineCode();
        Long versionNo = engineVersionReqVo.getVersionNo();
        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(engineCode);
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);

        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineCode, versionNo);
        if (engineVersion != null && engineVersion.getStatus().equals(EngineVersionStatusEnum.NEW.getStatus())) {
            return clear(engineVersion.getId());
        }
        throw new ApiException(ErrorCodeEnum.ENGINE_CLEAR_ERROR.getCode(), ErrorCodeEnum.ENGINE_CLEAR_ERROR.getMessage());
    }

    @Override
    public EngineVersionInOutVarsRespVo getInOutVarList(String engineCode, Long versionNo, Long loginUserId) {
        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineCode, versionNo);

        if (engineVersion == null) {
            throw exception(ErrorCodeEnum.ENGINE_NOT_EXISTS);
        }

        EngineVersionInOutVarsRespVo inOutVarsRespVo = new EngineVersionInOutVarsRespVo();
        List<VariableVO> variables = engineNodeComponentRelService.searchVariablesByEngineVersionId(engineVersion);
        if (variables == null || variables.isEmpty()) {
            return inOutVarsRespVo;
        }
        //查询变量依赖的输入变量
        List<String> varCodes = variables.stream().map(VariableVO::getVarCode).collect(Collectors.toList());
        List<VariableDepVO> variableDepVOS = variableService.findVarsDep(varCodes, loginUserId);

        variableDepVOS.forEach(variableDepVO -> {
            VariableVO variableVO = new VariableVO();
            BeanUtils.copyProperties(variableDepVO, variableVO);
            variableVO.setVarType(Integer.valueOf(variableDepVO.getVarType()));
            variableVO.setVarValueType(Integer.valueOf(variableDepVO.getValueType()));
            variableVO.setUpdateTime(DateUtils.dateToString(variableDepVO.getUpdateTime(), DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));
            variables.add(variableVO);
        });

        //去重
        List<VariableVO> distinctVariables = variables.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(VariableVO::getVarCode))), ArrayList::new));

        //按照输入、输出分类返回
        distinctVariables.forEach(variable -> {
            if (variable.getIsInput() != null && variable.getIsInput() == 1) {
                inOutVarsRespVo.getInVars().add(variable);

            } else if (variable.getIsOutput() != null && variable.getIsOutput() == 1) {
                inOutVarsRespVo.getOutVars().add(variable);
            }
        });
        inOutVarsRespVo.setAllVars(variables);
        return inOutVarsRespVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteVersion(EngineVersionReqVo engineVersionReqVo) {
        EngineVersion engineVersion = getByEngineCodeAndVersionNo(engineVersionReqVo.getEngineCode(), engineVersionReqVo.getVersionNo());

        if (engineVersion == null) {
            throw exception(ErrorCodeEnum.ENGINE_NOT_EXISTS);
        }

        Integer versionStatus = engineVersion.getStatus();
        if (!versionStatus.equals(EngineVersionStatusEnum.NEW.getStatus())) {
            throw exception(ENGINE_DELETE_ERROR, "只能删除状态为新建的版本，当前版本状态:" + EngineVersionStatusEnum.getName(versionStatus));
        }

        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(engineVersionReqVo.getEngineCode());
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);

        return this.removeById(engineVersion.getId());
    }

    @Override
    public EngineVersion getRunningVersion(String engineCode) {
        return getVersionByStatus(engineCode, EngineStatusEnum.DEPLOY.getStatus());
    }

    @Override
    public List<EngineVersion> getRunningVersionList(String engineCode, long versionNo) {
        return this.list(queryWrapperByVersionNo(engineCode, versionNo));
    }

    @Override
    public EngineVersion getVersionByStatus(String engineCode, Integer status) {
        return this.getOne(queryWrapper(engineCode, status));
    }

    public Long getVersionIdByStatus(String engineCode, Long engineVersionNo, Integer status) {
        EngineVersion engineVersion = this.getOne(
                new LambdaQueryWrapper<EngineVersion>()
                        .select(EngineVersion::getId)
                        .eq(EngineVersion::getEngineCode, engineCode)
                        .eq(EngineVersion::getVersionNo, engineVersionNo)
                        .eq(EngineVersion::getStatus, status));
        return Objects.nonNull(engineVersion) ? engineVersion.getId() : null;
    }

    @Override
    public EngineVersion getLatestEngineVersion(String engineCode) {
        QueryWrapper<EngineVersion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("engine_code", engineCode);
        queryWrapper.orderByDesc("version_no");
        return getOne(queryWrapper);
    }


    public boolean isRunning(Long versionId) {
        EngineVersion engineVersion = this.getById(versionId);
        if (engineVersion != null) {
            return engineVersion.getStatus().equals(EngineVersionStatusEnum.ENABLE.getStatus());
        }
        return false;
    }
}
